Skip to content

fix(types): SchemaRegistry's kanban entry stops describing a component it cannot name - #7662

Merged
os-sam merged 3 commits into
mainfrom
claude/issue-7645-schema-registry-kanban-honesty
Sep 5, 2026
Merged

fix(types): SchemaRegistry's kanban entry stops describing a component it cannot name#7662
os-sam merged 3 commits into
mainfrom
claude/issue-7645-schema-registry-kanban-honesty

Conversation

@os-sam

@os-sam os-sam commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7645

SchemaRegistry documents itself as "the Single Source of Truth for component
type lookups"
. Its 'kanban' entry named DeclarativeKanbanSchema, while the
renderer registered for that key is ObjectKanbanRenderer in
@object-ui/plugin-kanban, which consumes that package's own KanbanSchema.
For this one key the map's value did not describe the component the key names.

Option taken: B — the value stops asserting; the key is untouched. The
entry is now BaseSchema & { type: 'kanban' }: the strongest claim this layer
can prove, and one that is true of both dialects.

Why A was unreachable — three routes, all measured

A1 — import the honest type. check:phantom-deps, exit 1:

❌  1 import(s) are not declared where a consumer would find them (1 distinct package/dependency pair(s)):
      packages/types/src/registry.ts:9:1  [undeclared-runtime]  @object-ui/types imports '@object-ui/plugin-kanban' -> @object-ui/plugin-kanban (type-only)

Baseline for that reading, same gate, unmutated tree: ✅ Every in-scope import is declared by the package that publishes it. (exit 0). Type-only
imports are not exempt — the gate names the pair explicitly.

A2 — declare the dependency instead. Read off the manifests: @object-ui/types
has 0 workspace runtime deps, and @object-ui/plugin-kanban declares
@object-ui/types. Adding the edge closes the cycle
@object-ui/types -> @object-ui/plugin-kanban -> @object-ui/types. (Reader
proven live by the same pass returning 7 workspace deps for plugin-kanban.)

A3 — name a proxy this layer can already reach. @objectstack/spec/ui is a
declared dependency of @object-ui/types and does export ObjectKanbanProps,
mirroring the same renderer. It is not the same type: of the plugin
KanbanSchema's 19 members it shares 11, cannot express 8 (allowCollapse,
cardTemplates, className, columnWidths, limit, onCardMove,
onQuickAdd, and the type discriminant every other registry value carries),
and adds 2 the plugin does not have (filter, titleField). Pointing there
swaps one false claim for a differently false one, so it was rejected.

Relocating the plugin dialect into this layer is the remaining route. At the
time of the code commit it was the one objectui#6172's ruling (2026-08-31) had
declined, and the dispatch ruled out widening the published surface for a
latent defect, so it was not taken here. Since then objectui#7664's ruling (a)
(2026-09-05T02:09:40Z) has taken exactly that route — see "The record moved"
below; nothing of it is implemented in this PR.

Latency re-verified, with a lit control

The card's claim that nothing indexes the value side was re-measured, not
inherited. Repo-wide, excluding node_modules and dist, SchemaRegistry[
returns exactly 1 hit — prose in .changeset/3965-mint-box-neutral-container.md,
not a TypeScript indexed access. Control for the query shape, same command,
same file set: ComponentPropsMap[ returns 39. An earlier control for this
measurement read zero; it was discarded as a dark instrument and rebuilt rather
than published. Also measured: 0 gate scripts mention SchemaRegistry
(control: 36 hits for packages/types/src in the same corpus), and the
@object-ui/types ComponentType has no in-repo consumer but its own
declaration.

The pins, and proof they are lit

Type-level assertions are erased at runtime, so vitest says nothing about them.
The instruments are tsc -p packages/types/tsconfig.test.json and
tsc -p packages/plugin-kanban/tsconfig.test.json, both chained off their
packages' type-check scripts. Program membership proved with --listFiles:
each pin file appears once; the plugin project resolves @object-ui/types to
packages/types/dist/index.d.ts (0 hits for the source path), so
@object-ui/types was rebuilt before every reading on that side.

Three ablation legs, prediction written before each run:

Leg Mutation Predicted Observed
2 restore the defect ('kanban': DeclarativeKanbanSchema) non-zero, exactly 1 error, at _ValueIsNotTheDeclarativeFace exit 2, 1 error, …test.ts(77,7): error TS2344 — that pin, only that pin
3 delete the 'kanban' key non-zero, several errors incl. _KeyKept exit 2, 5 errors: line 72 (_KeyKept) plus TS2339 at 77/83/89
4 add a member the plugin face lacks, rebuild types non-zero at _PluginFaceSatisfiesTheEntry exit 2, 1 error, …test.ts(69,7): error TS2344

No prediction missed. Every mutation was proved on disk by anchored
before/after counts and git hash-object movement off the HEAD blob; every
restore by blob equality and an empty git diff HEAD. Leg 4's restore leg
also rebuilt and re-checked, confirming the marker left dist (1 -> 0) and the
plugin project returned to 0 errors — an unrebuilt restore would have left
every later reading measuring the wrong tree. One earlier attempt at leg 1
mutated only half of its target; the count-based guard caught it, the trap
restored, and it was re-run rather than quietly repeated.

Leg 3 is the one that matters most for the published surface: it is the proof
that keyof SchemaRegistry still yields 'kanban', and that deleting the key
— which also removes the false claim and leaves every runtime suite green — is
now a loud compile error.

Verification, at commit 41ff58ff

Union re-run after the final commit, on a clean tree. Each gate's own verdict
line:

  • check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.
  • check:control-bytes✅ check-control-bytes: OK (scanned 6255 tracked text file(s); skipped 85 binary).
  • check:changeset-presence✅ 3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)
  • check:changeset-no-major — verdict: no changeset declares a major bump
  • check:doc-types✅ Every documented component type is registered.
  • check:readme-exports✅ check-readme-exports: OK (… 0 unbuilt …)
  • check:dist-completeness✓ dist completeness: 12 package(s) complete (1609 emitted files verified)
  • check:spec-symbols✅ spec symbol derivation: 1344 files scanned against 4959 spec export names
  • check:node-esm-load, check:published-dist, check:entry-guard — exit 0
  • pnpm --filter @object-ui/types run type-check and the same for
    @object-ui/plugin-kanban — exit 0
  • pnpm exec vitest run packages/types/ packages/plugin-kanban/ — 126 files,
    1844 tests passed
  • eslint on the three touched files — exit 0

check:readme-exports first reported the population COLLAPSED -- this run proves nothing (24 of 40 packages unbuilt locally). That is a precondition
failure, not a red: all packages were built and it was re-run, which is the
green quoted above (0 unbuilt, 421 self-imports judged, up from 312
unjudgeable).

Declared narrowing. check:sdui-registration-pins is not measured here
— it reads the built console bundle and refuses to judge without one
(a run with nothing to read has measured nothing). It is not implicated: the
diff changes no registration. The full repo-wide lint and the remaining gate
farm are left to CI, which runs them unfiltered. Exit codes throughout were
captured by redirect before any pipe.

Contract review

Clause-②: yes. SchemaRegistry is published, and this changes the type of
one of its members — a falsifiable contract-semantics claim, not a spelling. It
adds no exported symbol and no key (the diff contains 0 changed export
lines), so keyof SchemaRegistry and the published ComponentType union are
byte-identical; what moves is the value side of one key.
needs:contract-review is on both carriers and this stays a draft — not
ready, not enqueued.

Semver: minor, and this is a breaking change for consumers outside this
repository. It ships as minor because this repo's version-alignment rule
(AGENTS.md §版本号策略) pins objectui's major to @objectstack's and ships
objectui's own breaking changes as minor with the break spelled out in the
changeset body — which, after the contract-review remediation below, it is:
the assignability break for consumers who indexed SchemaRegistry['kanban'],
and the silent any on member reads.

Remediation after contract review — commit 3748de9a

The isolated contract reviewer REFUSED on the changeset alone (code and pins
judged sound, bump level correct). Three defects in that one file, each
re-measured here before rewriting — merge-base tree 2ce2612d and head tree
41ff58ff, @object-ui/types rebuilt on both, predictions written before each
run, instrument proved to read packages/types/dist/index.d.ts (1 hit, 0 for
src/) by --listFiles:

  • Shared members (AST census through the TypeScript compiler API, identical
    on both trees since neither source moved): declarative face 7 body
    members, plugin face 19, shared names 3type, columns,
    onCardMove. onCardMove is type-identical on both sides (probe
    _A_OnCardMoveEqual passes on both trees; only parameter names differ);
    column element types are 6 vs 6 sharing 5. Controls: DeclarativeKanbanCard
    read 9, self-intersection read 7, a disjoint pair read 0. The changeset's
    "sharing only the type tag" was false and is corrected.
  • Assignability break: const s: DeclarativeKanbanSchema = registryValue
    compiles at base and fires TS2741 at head (Property 'columns' is missing). Lit controls fired on both trees: a GridSchema value and a
    number into the same target, TS2322 each. The changeset now names this as
    a break for consumers who indexed SchemaRegistry['kanban']; the bump stays
    minor per the repo rule.
  • Silent any-read: SchemaRegistry['kanban']['columns'] is
    DeclarativeKanbanColumn[] at base and any at head (probe pair
    _C_ColsIsAny / _C_ColsIsDecl, each the other's cross-control — exactly
    one fires per tree); draggable likewise. Undeclared keys already read
    any at base through the same BaseSchema index signature (control passes
    on both trees), so the change is to the declared members. The changeset now
    states the mechanism.

Probe totals matched prediction exactly — 5 errors at head, 6 at base, no
missed prediction. The reviewer's own recorded miss (item 5) reproduced:
const k: KanbanSchema = registryValue compiles on both trees, and the
reverse (plugin face into the registry value) is TS2322 at base — columns
optional vs required — and compiles at head. So the plugin-side pin proves the
tag and nothing narrower; neither the changeset nor this body claims more for
it.

Remediation diff, git diff --stat 41ff58ff7..3748de9af:
.changeset/7645-schema-registry-kanban-honesty.md | 46 ++++++++++++++++++-----
— 1 file, no source or test file moved. Gates re-run on the committed head
3748de9a, each's own verdict line: check-changeset-no-major✅ No changeset declares a major bump.; check-changeset-overwrite✅ No pre-existing changeset was modified or deleted.; check-changeset-presence
✅ 3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s); check:control-bytes✅ check-control-bytes: OK (scanned 6255 tracked text file(s); skipped 85 binary).; check:shell-escape-residue
✅ check-shell-escape-residue: OK. Session for this remediation:
https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3.

The record moved — round-2 contract review, commit 55a7470e9

The second isolated review (PR comment 5548793740) reproduced all twelve of the
first review's judgments and graded the code and the minor bump correct, then
REFUSED on one factual claim that went false while it was being written: the
changeset, the comment above the entry in registry.ts, and both pin docblocks
said the types-vs-plugin gap was "permanent by decision". The record:

  • objectui#7664 was ruled (a) at 2026-09-05T02:09:40Z (comment 5548643216,
    director seat, maintainer verbatim 「同意」, decision batch Add 8-directional resize system and premium Tailwind design to designer #41). Under (a) the
    @object-ui/types 'kanban' arm is rewritten to the plugin's shape,
    @object-ui/plugin-kanban conforms to that declaration, the
    DeclarativeKanban* trio retires under ADR-0049, and registry.ts is on the
    execution list. The ruling itself notes that it reverses the "keep both faces"
    half of objectui#6172 (2026-08-31). So this PR's BaseSchema & { type: 'kanban' }
    entry is the transitional state under that ruling — honest at this commit,
    scheduled to be re-pointed at the declared type — not a permanent one. The
    remediation commit 3748de9af (02:18:01Z) post-dated the ruling by 8m21s and
    carried the stale phrase. Nothing of (a) is implemented here.
  • objectui#7651 is closed not_planned (ruled B, 2026-09-05T02:09:54Z), not
    "an open maintainer decision".

Commit 55a7470e9 corrects the tense in all four carriers in the tree — the
changeset paragraph, registry.ts (comment above the entry only), the types pin
docblock, the plugin pin docblock and its _StillTwoDialects comment (the latter
beyond the review's five items: "if these ever converge" is now a scheduled
event, same class) — and this body corrects the two claims above. Every tense
was written after reading the card's current state and its ruling comment.

Wording only, proved: the TypeScript token stream (scanner with trivia —
whitespace and comments — dropped) of each of the three .ts files is
identical before and after, 496 / 231 / 212 tokens, 0 differing; lit control:
the same instrument on a scratch copy with 'kanban' spelled 'kanbam' on the
type line reports exactly one differing token. registry.ts:218 is
byte-identical (sha256 7fbfa7bd… before and after) and every code file keeps
its line count (228 / 94 / 84), so the pins' cited lines (69/72/77/83/89) are
where the prior evidence cites them. Blob ids moved for all four files, so the
comparison was of changed files. Phrase census after the edit, predictions
written first: "permanent by decision" 0 (from 3), "open maintainer decision"
0 (from 1), 7664 5 (from 0); one prediction missed — the lit control for the
census (TRANSITIONAL) read 2 where 3 was predicted (the plugin pin says
"schedules their convergence" and the changeset uses lowercase).

Both pins still read exit 0 through their declared instruments on the
committed head 55a7470e9 (dirty-files 0), @object-ui/types rebuilt first:
tsc -p packages/types/tsconfig.test.json --listFiles — exit 0, 0 errors, pin
file 1, registry.ts 1; tsc -p packages/plugin-kanban/tsconfig.test.json --listFiles — exit 0, 0 errors, pin file 1, packages/types/dist/index.d.ts
1, packages/types/src/ 0. Lit control per instrument, prediction first: a
scratch __lit7645__.test.ts asserting Equal of 1 and 2 dropped into each
__tests__ dir — exit 2, exactly 1 error, error TS2344 at (3,24) in that
file, both projects; removed, git status back to the four edited files only.

Gate union on 55a7470e9, each gate's own verdict line: check:control-bytes
✅ check-control-bytes: OK (scanned 6255 tracked text file(s); skipped 85 binary).;
check-changeset-presence✅ 3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s); check-changeset-no-major
✅ No changeset declares a `major` bump.; check-changeset-overwrite✅ No pre-existing changeset was modified or deleted.; check:shell-escape-residue
✅ check-shell-escape-residue: OK; vitest on the two pin files — Test Files 2 passed (2); eslint on the three .ts files — exit 0. check:phantom-deps is
not re-run: the import token streams are unchanged (declared narrowing). Exit
codes captured by redirect before any pipe.

For the PM, not edited here: #7664's own sequencing note calls PR #7662
"already merged per the card"; it is open and draft.

Scope

Untouched, as fenced: packages/plugin-kanban's own KanbanSchema (the face
objectui#6172 kept — the new file there only reads it), KanbanSchema.data
(objectui#7651 — ruled B and closed as not_planned at 2026-09-05T02:09:54Z:
no record-source ladder, data stays a raw-row input; nothing here reads it), scripts/check-doc-links.mjs and
scripts/github-slug.mjs (#7644), and the getDataConfig producers (#7632).

The card also asks whether the one-authority family rule should be extended so
that every SchemaRegistry value must be the type its registered renderer
honours. That is not answered here: this PR settles the 'kanban' key, and the
generalization lives on as objectui#7665 (open, pm:queue when this body was
written), which takes the cheap option C — narrow the docblock's promise to the
key set — while the sweep of the other keys stays unmeasured.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3

…t it cannot name

`SchemaRegistry` advertises itself as the Single Source of Truth for component
type lookups, but its `'kanban'` entry named `DeclarativeKanbanSchema` — this
package's authoring/validation face — while the renderer registered for that
key is `ObjectKanbanRenderer` in `@object-ui/plugin-kanban`, which consumes
that package's own `KanbanSchema`. Unrelated dialects, so for this one key the
map's value did not describe the component the key names (objectui#7645).

Correcting the value is not reachable from this layer, measured rather than
assumed: importing the plugin is a phantom dependency (`check:phantom-deps`
rejects the pair, type-only included), and declaring the dependency closes the
cycle `@object-ui/types` -> `@object-ui/plugin-kanban` -> `@object-ui/types`.
objectui#6172's ruling kept the plugin's bare names rather than relocating the
dialect into this zero-workspace-dependency layer, so the gap is by decision.

The entry therefore asserts only what this layer can prove and what both
dialects satisfy: `BaseSchema & { type: 'kanban' }`. `keyof SchemaRegistry` is
unchanged — removing the key would silently narrow the published
`ComponentType` union — and two compile-time pins hold that shape: one here
(the key survives; the value no longer claims the declarative face) and one in
`@object-ui/plugin-kanban`, the only package that can name both sides, proving
the renderer's own `KanbanSchema` satisfies what the entry now asserts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3180.1 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-CI1E9WNz.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 510.70KB 116.21KB
core (index.js) 6.70KB 2.68KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.27KB 61.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 48.05KB 13.34KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.86KB 34.68KB
plugin-designer (index.js) 212.86KB 43.19KB
plugin-detail (index.js) 250.55KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.41KB 41.04KB
plugin-grid (index.js) 209.40KB 56.80KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.57KB 6.82KB
plugin-markdown (index.js) 13.70KB 4.68KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.35KB 3.23KB
plugin-view (index.js) 85.24KB 20.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

os-sam commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

契约复核 — 裁决:REFUSE(逐字采纳)

归属:domain:ui 派发席位,依 2026-08-31 裁定席内完成;董事席链条为可选事后审计,非放行必要条件;⛔ 无「等外部席位」的无限期状态。

档位与核验:隔离 fable 子代理(⛔ 契约复核不适用额度耗尽豁免降档)。transcript 中 harness 逐消息盖章的 model 字段 76 次读数全部 claude-fable-5-1,0 次回退。亮控:同一 grep 在本会话其它 transcript 上读出 claude-opus-5(30 份)与 <synthetic>(1 份)—— 该 grep 能读出非 fable 值,所以这里的零是读数。达标 ⇒ 逐字采纳。父会话只有两个合法动作:逐字采纳或整体作废;⛔ 不修剪、不润色、不挑着采纳。

独立性:只喂卡片 #7645、既有裁定与 PR 本身;⛔ 未喂派发简报、⛔ 未喂本席结论;简报按对抗性写法。

needs:contract-review 两个载体都不清除。 REFUSE 就是 REFUSE。补救是纯文字、限于 37 行的 changeset 一个文件,代码与 pin 复核判为 sound。补救推上去后重跑契约复核,通过再清标。

一处已知渲染损耗:下文有三处尖括号泛型(Equal<…>Extract<…>、箭头函数类型),GitHub 正文净化器会吞掉尖括号内容。原文以本地 transcript 为准;⛔ 此处不改字,只作说明。


Contract review — objectui PR #7662 (card #7645)

Head 41ff58ff, base f7cf7e8a, merge-base with current main 2ce2612d; no drift on any touched type file; merge-tree clean. 32 CI checks: all success or skipped. Everything below was measured on a detached worktree at the head sha plus a second at the merge-base, with my own tsc probe battery (base leg exit 0, head leg exit 0, and a head-probe-on-base cross-control that fired on exactly the six predicted lines — the instrument is lit).

① Derived judgments

  1. SchemaRegistry['kanban'] value: DeclarativeKanbanSchemaBaseSchema & { type: 'kanban' } (packages/types/src/registry.ts; external view confirmed in built dist/registry.d.ts:86). RIGHT as a type decision: A measured unreachable (phantom-deps gate doc line 127 says type-only imports are STRICT; head tree exit 0 with its verdict line; types has 0 workspace deps while plugin-kanban declares types), and both faces satisfy the new value (my P1/P2 pass against dist).
  2. Accept-set as an assignment target: widened. { type: 'kanban' } alone is rejected at base (TS2741, missing columns) and accepted at head; { type:'kanban', draggable, objectName, garbage: 42 } accepted at head; { type: 'grid' } still rejected. Named in the PR body ("the value stops asserting") — RIGHT. Not named: that BaseSchema's [key: string]: any (base.ts:440, inside BaseSchema, closes at 441) is what admits arbitrary keys.
  3. Read side: SchemaRegistry['kanban']['columns'] goes from DeclarativeKanbanColumn[] to any (H4: Equal<typeof cols, any> false at base, true at head). NOT NAMED by PR body or changeset. "Gets the base node shape instead" is literally true, but the consumer-visible effect is a silent type-safety loss, not an error. WRONG by omission in the migration note.
  4. Assignability SchemaRegistry['kanban']DeclarativeKanbanSchema: compiles at base, TS2741 at head (H6). This is a compile break for any external consumer that flowed the registry value into the declarative face. The changeset's "this is a correction rather than a break" is WRONG.
  5. SchemaRegistry['kanban'] ↔ plugin KanbanSchema: mutually assignable at head. My prediction that the entry would not flow into the plugin face MISSED (unused @ts-expect-error at pk-probe.ts:15) — the plugin face is all-optional except type, so the index signature satisfies it. Not a defect; it does mean the plugin-side pin proves nothing beyond the tag.
  6. keyof SchemaRegistry / published ComponentType: byte-identical. 69 keys base and head, 'kanban' present; textual key-list diff identical, with a control that fires when kanban is removed; Extract<ComponentType,'kanban'> = 'kanban' on both trees. No silent narrowing — RIGHT.
  7. Exported symbols: 0 export statements changed (the two grep hits for "export" are prose: changeset line 40, registry comment line 287). DeclarativeKanbanSchema still exported (index.ts:294) and still the ComplexSchema arm (complex.ts:1212). RIGHT.
  8. Two new test files under src/__tests__/: not published (tsconfig.json excludes tests from dist; tsconfig.test.json is emit-free). RIGHT — no surface. Changeset-presence gate is presence-only, not per-package (script line 408), and plugin-kanban is in the same fixed group.
  9. The PR's own pins are lit and in-program: tsc -p packages/types/tsconfig.test.json exit 0, pin file and registry.ts each once in --listFiles; the plugin pin run in isolation through the plugin's own tsconfig.test.json exit 0, resolving @object-ui/types to dist (1) not src (0). The whole-package plugin run is a precondition failure (45× TS2307 for unbuilt siblings), not a reading. Note the "Leg 3" invariant is redundantly held by my own H1/B1.
  10. Latency claim re-measured: non-test SchemaRegistry\s*\[ = 0 at base and head; 6 at head are the PR's own pins; control ComponentPropsMap[ = 25. No @object-ui/types ComponentType consumer outside its own declaration (control: 136 React ComponentType hits). No SchemaRegistry import from @object-ui/types anywhere in the objectstack sibling (its 30 hits are objectql's own class). RIGHT.
  11. A3 (ObjectKanbanProps) numbers reproduce exactly: 13 keys, shares 11, cannot express the 8 named, adds filter/titleField. RIGHT. (My first reading of this was a dark instrument — regex slid into ObjectCalendarPropsSchema — discarded and rebuilt.)
  12. Fences honoured: plugin-kanban/src/types.ts untouched; no content/docs/releases/; 4 files in the diff.

② Semver grading

Declared: '@object-ui/types': minor (.changeset/7645-schema-registry-kanban-honesty.md, line 2).

Level: correct. AGENTS.md §版本号策略 line 239 is the standing rule: "changeset 里不要声明 major … objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)", enforced by scripts/check-changeset-no-major.mjs. So "needs a major bump" is the wrong conclusion in this repo even though the change is breaking (item 4). The rule's second half — the body must spell out the breaking semantics — is where the changeset fails:

  • It gives a migration ("should name the face it actually means") but then states "which is why this is a correction rather than a break" — contradicted by H6 (loud compile break) and H4 (silent any).
  • It omits the index-signature mechanism that makes member reads collapse to any without an error.
  • Measured false: "19 members against the declarative face's own set, sharing only the type tag." Declarative own members are 7, plugin 19; shared by name: type, columns, onCardMove; onCardMove is (string, string, string, number) => void on both sides (only parameter names differ); columns is on both (required DeclarativeKanbanColumn[] vs optional KanbanColumn[], element types sharing 5 of 6 members).

③ Boundary-flag disposition

Verdict

VERDICT: REFUSE — the code and pins are sound and the bump level is correct, but the changeset (the PR's published release-note input) contains one measured-false factual claim ("sharing only the type tag") and mischaracterises a measured compile break for external indexed-access consumers as "a correction rather than a break", while omitting that BaseSchema's index signature turns member reads into silent any. Remediation is confined to that 37-line file: name it a break for consumers who indexed SchemaRegistry['kanban'], state the any-read effect, and correct the shared-member sentence.

Implemented-by: claude/issue-7645-schema-registry-kanban-honesty
Reviewed-by: isolated subagent (host session_01KbJQ1y1J12nZxYzFWhP8Q3)


Generated by Claude Code

…nt any-read

Contract review (PR #7662) refused the changeset alone: the code and pins were
judged sound and `minor` correct, but the release-note input claimed the two
kanban dialects share "only the type tag" (measured: 3 shared names of 7 vs 19
body members - type, columns, onCardMove), called a measured compile break "a
correction rather than a break", and omitted that BaseSchema's index signature
turns the declarative members' reads into silent any.

Re-measured on the merge-base and head trees with lit controls before this
rewrite: SchemaRegistry['kanban'] -> DeclarativeKanbanSchema compiles at base
and is TS2741 at head; SchemaRegistry['kanban']['columns'] is
DeclarativeKanbanColumn[] at base and any at head. The changeset now states
both effects and keeps the bump at minor per the version-alignment rule.
No source or test file changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3180.1 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-CI4V3Zxi.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 510.70KB 116.21KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.27KB 61.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 47.87KB 13.31KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.86KB 34.68KB
plugin-designer (index.js) 212.86KB 43.19KB
plugin-detail (index.js) 250.55KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.26KB 41.00KB
plugin-grid (index.js) 209.29KB 56.78KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.44KB 6.78KB
plugin-markdown (index.js) 13.93KB 4.81KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.20KB 3.19KB
plugin-view (index.js) 85.24KB 20.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Contract re-review (round 2) — claude-fable-5-1, adopted verbatim

Isolated review subagent. Tier verified from its transcript: 56/56 assistant turns stamped claude-fable-5-1, 0 fallback notices. Lit control: the same reader returns claude-opus-5 5810 times across this session's 102 transcripts, so the zero is a reading. Adopted verbatim — not edited, trimmed or paraphrased.


Contract review — objectui PR #7662 (card #7645), head 3748de9af

Reviewer: independent, this session (session_01KbJQ1y1J12nZxYzFWhP8Q3 host; no GitHub writes made, no shared checkout edited). Base for every measurement: merge-base of the PR head against current origin/main (a3eb5d07a) = 2ce2612df; PR commits since it: 41ff58ff7 (code) and 3748de9af (remediation). Both sides built and measured on detached scratch worktrees fetched into private refs (refs/review/7662-*), since removed; the primary checkout /home/user/objectui was read-only throughout (0 modified files, HEAD unchanged at 77b3cf1f5). Toolchain: node 22.22.2, pnpm 10.31.0, tsc 6.0.2; pnpm-lock.yaml, root and both package package.json/tsconfig.json blob-identical on both trees, so the two dist builds are of one dependency graph.

Instrument. One probe file (packages/plugin-kanban/src/__review7662__/probe.ts, identical bytes on both trees) compiled through the plugin's own tsconfig.test.json settings (paths: {}), so @object-ui/types resolves to the built dist (--listFiles: packages/types/dist/index.d.ts 1, packages/types/src/ 0, plugin-kanban/src/types.ts 1, both trees). Predictions for all 22 markers were written into the file before either run. Lit controls, all four fired on both trees: L1 Equal<DeclarativeKanbanSchema['draggable'], string> (TS2344), L2 GridSchema into DeclarativeKanbanSchema (TS2322), L3 IsAny<DeclarativeKanbanSchema['columns']> (TS2344), L4 { type: 'grid' } into SchemaRegistry['kanban'] (TS2322). Four non-vacuity pins (IsAny false for SchemaRegistry, KanbanSchema, DeclarativeKanbanSchema, the entry) passed on both. Totals: base exit 2 / 11 errors, head exit 2 / 7 errors — exactly as predicted, no marker missed. Every zero below is a reading from an instrument that produced non-zero on the same run.

① Derived judgments

  1. SchemaRegistry['kanban']: DeclarativeKanbanSchemaBaseSchema & { type: 'kanban' }. Published view: dist/registry.d.ts:85:86-88; a diff -rq of the whole @object-ui/types dist between trees names registry.d.ts as the only differing file (plus the import lines that move with it). RIGHT as a type decision. A1 re-measured by ablation, not read from the gate's docs: check:phantom-deps exit 0 clean (✅ Every in-scope import is declared…), exit 1 with one type-only import added to registry.ts (packages/types/src/registry.ts:1:1 [undeclared-runtime] @object-ui/types imports '@object-ui/plugin-kanban' … (type-only)), blob restored to HEAD and git diff HEAD -- packages/ empty. A2 by my own manifest read: @object-ui/types 0 workspace deps, @object-ui/plugin-kanban 7 including @object-ui/types, so the edge is a cycle. Both faces satisfy the new value (M3 declarative→entry, M15 KanbanSchema extends entry: pass at head).
  2. Accept set as an assignment target: widened. { type: 'kanban' } TS2741 at base, accepted at head (M11); { type: 'kanban', garbage: 42 } TS2741 at base, accepted at head (M13); { type: 'grid' } TS2322 on both (L4). The changeset names the mechanism ("the entry lacks the required columns member") — RIGHT.
  3. Assignability entry → DeclarativeKanbanSchema: compiles at base, TS2741 at head (M2: Property 'columns' is missing in type 'BaseSchema & { type: "kanban"; }'). Changeset now calls it "Breaking, loudly, for consumers who indexed SchemaRegistry['kanban']" — RIGHT. (The prior REFUSE's item 4, remediated.)
  4. Member reads collapse to any through BaseSchema's index signature (base.ts:440). entry['columns']: DeclarativeKanbanColumn[] at base, any at head (M6a/M6b, each the other's cross-control, exactly one fires per tree); entry['draggable'] likewise (M7); undeclared keys any on both trees (M8); entry['className'] stays string | undefined on both (M17, BaseSchema declares it). Changeset states the mechanism, the columns before/after, and the undeclared-keys caveat — RIGHT. (Prior item 3, remediated.)
  5. Plugin face ↔ entry. KanbanSchema → entry: TS2322 at base, compiles at head (M4); entry → KanbanSchema: compiles on both (M5). So the plugin-side pin proves the tag and nothing narrower; neither changeset nor body claims more — RIGHT.
  6. keyof SchemaRegistry / published ComponentType: unchanged. 69 keys on both commits (source via git show, and 69 in each dist/registry.d.ts), lists identical; control: dropping 'kanban' from the head list → 1 differing line. M9 Extract<ComponentType,'kanban'> = 'kanban' on both. RIGHT.
  7. Exported symbol set of dist/index.d.ts: IDENTICAL between trees; DeclarativeKanbanSchema still exported (1). The two new test files are excluded from dist (**/__tests__/** in both packages' tsconfig.json; no dist/__tests__). No surface added — RIGHT.
  8. Changeset member census (7 / 19 / shared 3 = type, columns, onCardMove; columns 6 / 6 sharing 5; onCardMove type-identical). My AST census through the TypeScript compiler API reproduces every number; controls: DeclarativeKanbanCard 9, self-intersection 7, disjoint pair (DeclarativeKanbanSchemaKanbanColumn) 0, nonexistent interface null; M16 confirms onCardMove Equal on both trees. RIGHT. (Prior item "sharing only the type tag", remediated.) Precision note, not a defect: "the declarative draggable / onCardClick have no counterpart" omits the other two declarative-only members, onColumnAdd?: never and onCardAdd?: never (complex.ts:165/173, retired tombstones) — the "7" counts them.
  9. A3 (ObjectKanbanProps from @objectstack/spec/ui): 13 keys; shares 11 with the plugin's 19; cannot express 8 (allowCollapse cardTemplates className columnWidths limit onCardMove onQuickAdd type); adds 2 (filter titleField). PR body reproduces exactly — RIGHT.
  10. Latency: SchemaRegistry\s*\[ repo-wide (excl. node_modules, dist): 0 non-pin hits; 6 hits, all in the PR's two pin files; control ComponentPropsMap\[ 32. Generic SchemaRegistry[K] outside tests: 0. Sibling /home/user/objectstack: 0 imports of SchemaRegistry from @object-ui/types (control: 851 mentions of the bare name there). "Nothing in this repository performed such an indexed access" — RIGHT.
  11. The PR's pins are lit and in-program. Types pin via tsc -p packages/types/tsconfig.test.json --listFiles: exit 0, pin 1, registry.ts 1, 407 files. Plugin pin isolated through tsconfig.test.json settings: exit 0, dist 1 / src 0. My three ablation legs, predictions written first, all hit: A restore 'kanban': DeclarativeKanbanSchema (+ its import) → exit 2, exactly 1 error, test.ts(77,7); B delete the key → exit 2, 5 errors, (72,28) + TS2339 at 77/83/89; C add a member the plugin lacks, rebuild dist (marker 0→1→0) → plugin pin exit 2, 1 error (69,7), back to exit 0 after restore-and-rebuild. Every mutation proved by git hash-object movement and anchored counts, every restore by blob equality and empty git diff HEAD -- packages/. RIGHT.
  12. Changeset's account of the gap. "objectui#6172's ruling kept the plugin's bare names rather than relocating that dialect down here" — RIGHT (comment 5474954379, 2026-08-31, option A; option C "promote the plugin's dialect INTO types" was on the table and not taken). "…so the gap is permanent by decision" — WRONG on the record. finding(types/plugin-kanban): an authored kanban document can PASS safeValidateSchema and still render empty — the validator and the renderer honour different faces #7664 comment 5548643216 (director seat, maintainer verbatim 「同意」, batch Add 8-directional resize system and premium Tailwind design to designer #41, 2026-09-05T02:09:40Z) rules (a): the @object-ui/types 'kanban' arm is rewritten to the plugin's shape, plugin-kanban imports it, registry.ts is in the execution list, state pm:queue. That is the relocation the changeset calls permanently declined. The phrase was in the changeset from 41ff58ff7 (line 23) and was carried unchanged into 3748de9af (line 29), authored 02:18:01Z — 8m21s after the ruling; it also stands at registry.ts:201 and the types pin docblock line 32.
  13. Scope of the remediation, by measurement. git diff-tree -r --name-status 41ff58ff7 3748de9af → exactly one path, M .changeset/7645-schema-registry-kanban-honesty.md. Tree ids for packages, packages/types, packages/plugin-kanban, scripts, apps identical between the two commits; .changeset tree id moved ebcdbec46e66 → dba7d9603c61 (the control that proves the comparison can read a difference); root trees d33b9845… → 63928a33…. The "one markdown file, no source/test/behaviour change" claim is RIGHT. Whole PR vs merge-base: 4 paths; packages/plugin-kanban/src/types.ts, packages/types/src/complex.ts, packages/types/src/base.ts blob-identical; nothing under content/docs/releases/ (from the name-status listing; my tree-id probe of that path was unresolvable at both commits and is discarded as a dark reading).

On the prior REFUSE: every one of its twelve derived judgments and its three changeset defects reproduce under my instrument; none was wrong. The three defects are fixed at head (item 8: three shared names; item 3: the break named with TS2741; item 4: the any-read mechanism named). My measurement does not disagree with it anywhere.

② Semver grading

Declared: '@object-ui/types': minor (changeset line 2). Level: correct. AGENTS.md §版本号策略 (line 239): 「changeset 里不要声明 major … objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」, enforced by scripts/check-changeset-no-major.mjs: exit 0 at head (✅ No changeset declares a \major` bump.); lit control with a planted majorchangeset → exit 1 with the gate's own❌ A changeset declares a `major` bump(control file removed, tree clean). The rule's second half — spell the break out in the body — is now satisfied for both measured effects (the "Breaking, loudly" bullet with TS2741, the "Silent, for member reads" bullet with the index signature, and the closing paragraph naming it a breaking change shipped asminor). @object-ui/plugin-kanbancorrectly has no entry (unpublished test file only; samefixedgroup). CIChangeset Bump Policy, Changeset Declaration, Changeset Overwrite Report`: success at head. The one defect in the body is item 12 — a factual claim about the gap's future, not a bump claim.

③ Boundary-flag disposition

VERDICT: REFUSE

What must change (all wording; the type line registry.ts:218 stays byte-identical and both pins must still read exit 0 through their declared instruments after the edit):

  1. .changeset/7645-schema-registry-kanban-honesty.md line 29 — remove the claim of permanence. State the gap as decided by objectui#6172 (2026-08-31) and now scheduled to close under objectui#7664's ruling (a) (2026-09-05), under which this entry is transitional and will be re-pointed at the declared type. No number, bullet, or the bump needs to move.
  2. packages/types/src/registry.ts:201 — same phrase ("so the gap is permanent by decision, not by oversight"), same correction; comment-only.
  3. packages/types/src/__tests__/schema-registry-kanban-honesty-7645.test.ts:32 — same phrase, same correction; comment-only.
  4. packages/plugin-kanban/src/__tests__/schema-registry-kanban-honesty-7645.test.ts:33 and the PR body's Scope line — objectui#7651 is closed (ruled B, 2026-09-05T02:09:54Z), not "an open maintainer decision".
  5. PR body — record that finding(types/plugin-kanban): an authored kanban document can PASS safeValidateSchema and still render empty — the validator and the renderer honour different faces #7664 was ruled (a) at 02:09:40Z and that this PR's entry is the transitional state under it, so the second carrier the remediation claimed to fix is honest on the record as well.

Generated by Claude Code

…664 (a), not permanent

Wording only. The changeset, the comment above SchemaRegistry['kanban'] in
registry.ts, and both pin docblocks said the types-vs-plugin kanban gap was
"permanent by decision". objectui#6172's ruling (2026-08-31) kept the plugin's
bare names; objectui#7664's ruling (a) (2026-09-05T02:09:40Z) reverses that
half — the @object-ui/types 'kanban' arm is rewritten to the plugin's shape,
plugin-kanban conforms to it, the DeclarativeKanban* trio retires, and
registry.ts is on its execution list — so this PR's BaseSchema & { type:
'kanban' } entry is the transitional state under that ruling. The plugin pin
also called objectui#7651 "an open maintainer decision"; it was ruled B and
closed not_planned at 2026-09-05T02:09:54Z.

No executable line moves: the TypeScript token stream (trivia dropped) of all
three .ts files is identical before and after, the type line registry.ts:218
is byte-identical, and every file keeps its line count so the pins' cited
lines stay put. Both pins read exit 0 through their declared instruments
(tsc -p packages/types/tsconfig.test.json, tsc -p
packages/plugin-kanban/tsconfig.test.json against the rebuilt dist).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3180.1 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-CI4V3Zxi.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 510.70KB 116.21KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.27KB 61.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 47.87KB 13.31KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.86KB 34.68KB
plugin-designer (index.js) 212.86KB 43.19KB
plugin-detail (index.js) 250.55KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.26KB 41.00KB
plugin-grid (index.js) 209.29KB 56.78KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.44KB 6.78KB
plugin-markdown (index.js) 13.93KB 4.81KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.20KB 3.19KB
plugin-view (index.js) 85.24KB 20.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Contract re-review (round 3) — claude-fable-5-1, adopted verbatim

Isolated review subagent. Tier verified from its transcript: 89/89 assistant turns stamped claude-fable-5-1, 0 fallback notices, against a lit control that reads claude-opus-5 in this session's other transcripts. Adopted verbatim — not edited, trimmed or paraphrased.

Third round. Rounds 1 and 2 both refused; this reviewer re-measured both of their grounds and confirms neither was wrong and both are fixed.


Contract review (round 3) — objectui PR #7662 (card #7645), head 55a7470e9

Reviewer: independent, this session; no GitHub writes; shared checkouts read-only throughout (/home/user/objectui HEAD 77b3cf1f5 unchanged, 0 dirty before and after; /home/user/objectstack 0 dirty). Three detached scratch worktrees (head 55a7470e9, previous head 3748de9af, current origin/main 6eebc54b6 fetched first), installed offline from the pnpm store, @object-ui/types built on head and main, plugin-kanban's seven workspace siblings built on head under os-verify-lock.sh (VERDICT command-exit 0); all three trees removed afterwards, 0 review worktrees remain. Toolchain node 22.22.2, pnpm 10.31.0, tsc 6.0.3.

Record state read live, not inherited. PR #7662: open, draft: true, merged: false, mergeable_state: clean; git merge-tree --write-tree origin/main 55a7470e9 exit 0; 32 check runs at head — 29 success, 3 skipped, 0 failed, 0 in progress. Merge-base with current main 2ce2612df; main has moved but touches none of the PR's four paths; registry.ts has no diff merge-base→main, and complex.ts's only main-side hunk is FloatingChatbotConfig (line 922), not the kanban region. #7664: open, pm:queue, unassigned; ruled (a) in comment 5548643216 at 2026-09-05T02:09:40Z (director seat, maintainer 「同意」, batch #41), execution list names registry.ts; its parenthetical "already merged per the card" is false and was corrected in comment 5548799174 at 02:39:35Z, which holds the card until #7662 lands. #7651: closed not_planned at 02:09:54Z, ruled B (5548644219). #6172: closed completed 2026-09-04T15:01:34Z by PR #7643 (merged); ruling 5474954379 at 2026-08-31T07:07:38Z, option A. #7665: open, pm:queue.

Instrument. One probe file (identical bytes both trees, sha fbb25064…), 22 markers, predictions written into the file before either run, compiled through the plugin's own tsconfig.test.json settings so @object-ui/types resolves to dist (--listFiles: dist/index.d.ts 1, src/ 0, plugin types.ts 1, both trees). Four lit controls fired on both trees (TS2344/TS2322/TS2344/TS2322), four non-vacuity pins passed on both. Head: exit 2, 7 errors; main: exit 2, 10 errors — every error at the predicted marker, no miss. One recorded miss elsewhere: my lit-control column prediction (3,24) was inherited from the author's file; my file reads (3,27) — exit, count, code and line all hit.

① Derived judgments

  1. SchemaRegistry['kanban']: DeclarativeKanbanSchemaBaseSchema & { type: 'kanban' } (registry.ts:218; published view dist/registry.d.ts:86-87). RIGHT. A1 re-ablated: check:phantom-deps exit 0 clean with its verdict line, exit 1 with one type-only import injected — registry.ts:10:1 [undeclared-runtime] @object-ui/types imports '@object-ui/plugin-kanban' … (type-only); restored to blob b4b0b62a, git diff HEAD -- packages/ empty. A2 from manifests: @object-ui/types 0 runtime workspace deps, plugin-kanban 7 including types — the edge is a cycle. Both faces satisfy the entry at head (M3, M14 pass).
  2. Accept set as an assignment target: widened. { type: 'kanban' } TS2741 on main, accepted at head (M11); { type: 'kanban', garbage: 42 } TS2741 on main, accepted at head (M12); { type: 'grid' } TS2322 on both (L4). Changeset names the mechanism. RIGHT.
  3. Entry → DeclarativeKanbanSchema: compiles on main, TS2741 at head (Property 'columns' is missing in type 'BaseSchema & { type: "kanban"; }' but required in type 'DeclarativeKanbanSchema', M2). Changeset: "Breaking, loudly, for consumers who indexed SchemaRegistry['kanban']". RIGHT.
  4. Member reads collapse to any through BaseSchema's [key: string]: any (base.ts:440). entry['columns']: DeclarativeKanbanColumn[] on main, any at head (M6b/M6a, each the other's cross-control); draggable likewise (M7); undeclared keys any on both (M8); className stays string | undefined on both (M13). Changeset states mechanism, before/after and the undeclared-keys caveat. RIGHT.
  5. Plugin face ↔ entry. KanbanSchema → entry TS2322 on main, compiles at head (M4/M14); entry → KanbanSchema compiles on both (M5). The plugin-side pin proves the tag and nothing narrower; the record claims no more. Leg C (below) shows the pin is not vacuous. RIGHT.
  6. keyof SchemaRegistry / published ComponentType: unchanged. 69 keys on head and main, lists identical; control — dropping kanban from the head list gives 2 differing lines; Extract<ComponentType,'kanban'> = 'kanban' on both (M9). RIGHT.
  7. Exported symbols: unchanged. PR diff vs merge-base has 0 changed lines containing export under packages/*/src; the only non-comment changes in registry.ts are the BaseSchema import, the dropped DeclarativeKanbanSchema import, and line 218. DeclarativeKanbanSchema still exported (index.ts:294) and still the ComplexSchema arm (complex.ts:1212). RIGHT.
  8. New pins are unpublished. Both tsconfig.jsons exclude **/__tests__/**; dist contains 0 test paths. RIGHT.
  9. Pins lit and in-program through their declared instruments. Types: tsc -p packages/types/tsconfig.test.json --listFiles exit 0, 0 errors, pin 1, registry.ts 1, 563 files. Plugin: tsc -p packages/plugin-kanban/tsconfig.test.json --listFiles on the full project with siblings built — exit 0, 0 errors, pin 1, dist/index.d.ts 1, src/ 0, 1492 files (the unbuilt-sibling run is a precondition failure: 45× TS2307, 0 errors in the pin). Lit control per instrument: a scratch Equal<1,2> file → exit 2, exactly 1 error, TS2344, both. Ablation legs, predictions first, all hit: A restore 'kanban': DeclarativeKanbanSchema (registry.ts from main 6eebc54b6) → exit 2, exactly 1 error test.ts(77,7) TS2344; B delete the key → exit 2, 5 errors: (72,28) TS2344 _KeyKept, (77,7) TS2344 + (77,34) TS2339, (83,60), (89,54) TS2339; C add a member the plugin lacks, rebuild dist (marker 0→1) → plugin pin exit 2, exactly 1 error (69,7) TS2344; restore + rebuild → marker 0, exit 0. Every mutation proved by blob movement, every restore by blob equality and empty git diff HEAD -- packages/. RIGHT.
  10. Latency. SchemaRegistry\s*\[ repo-wide (excl. node_modules/dist): 0 non-pin TypeScript hits; 6 hits in the two pins; 5 prose hits (4 in this PR's changeset, 1 in 3965-…). Controls: ComponentPropsMap\[ 32; gate scripts mentioning SchemaRegistry 0 vs packages/types/src 17; sibling /home/user/objectstack 0 imports of SchemaRegistry from @object-ui/types vs 851 bare mentions. "Nothing in this repository performed such an indexed access" — RIGHT.
  11. A3 (ObjectKanbanProps, @objectstack/spec/dist/ui/index.d.ts:6617). 13 keys; shares 11 with the plugin's 19; cannot express 8 — allowCollapse cardTemplates className columnWidths limit onCardMove onQuickAdd type; adds filter, titleField. Controls: self 19, empty 0. PR body reproduces exactly. RIGHT.
  12. Changeset member census (AST via TypeScript compiler API, head and main identical — plugin-kanban/src/types.ts and base.ts blob-identical head vs main): DeclarativeKanbanSchema 7, plugin KanbanSchema 19, shared 3 (type, columns, onCardMove), column types 6/6 sharing 5; columns required DeclarativeKanbanColumn[] (complex.ts:134) vs optional KanbanColumn[] (types.ts:140); onCardMove type-identical (M15). Controls: DeclarativeKanbanCard 9, self-intersection 7, disjoint pair 0, nonexistent null. RIGHT.
  13. Registered renderer. ComponentRegistry.register('kanban', ObjectKanbanRenderer, …) at plugin-kanban/src/index.tsx:434-436; ObjectKanban.tsx:31 imports KanbanSchema from ./types. RIGHT.
  14. Wording-only scope 3748de9af55a7470e9. TypeScript scanner, trivia dropped: registry.ts 496/496, types pin 231/231, plugin pin 212/212 — 0 differing tokens each. Lit control: a scratch copy with 'kanban''kanbam' on line 218 reads exactly 1 differing token (docs: prioritize 🔮 Next Phase (N.1–N.5) as current active development focus #469 StringLiteral); trivia-kept control reads 12 differing. Line 218 sha256 7fbfa7bd6fd88f6b both heads; line counts 228/94/84 both heads; every changed registry.ts line is a // line (0 non-comment). Blobs moved for all four files. Phrase census head: "permanent by decision" 0 (prev 3), "open maintainer decision" 0 (prev 1), 7664 5 (prev 0), TRANSITIONAL 2 + lowercase 1. RIGHT — matches the PR body's figures exactly.
  15. The record's account of the gap. "objectui#6172's ruling (2026-08-31) kept the plugin's bare names rather than relocating that dialect down here" — RIGHT (5474954379, option A). "objectui#7664's ruling (a) (2026-09-05) rewrites this package's 'kanban' arm to the plugin's shape, has @object-ui/plugin-kanban conform to it, and retires the DeclarativeKanban* trio; registry.ts is on its execution list" — RIGHT, each clause is in 5548643216. "so this entry is scheduled to be re-pointed at the declared type … transitional state under that ruling, not the permanent one" — RIGHT: the PM's own hold comment says "fix(types): SchemaRegistry's kanban entry stops describing a component it cannot name #7662's entry is the transitional state under (a)" and "either way, (a) re-points this entry"; finding(types/plugin-kanban): an authored kanban document can PASS safeValidateSchema and still render empty — the validator and the renderer honour different faces #7664 is pm:queue, held for this PR. Nothing claims an executor, a date, or that (a) is dispatched. registry.ts's "Until then" and the pins' "when it lands … this pin retires with the re-point" keep the tense future. Plugin pin's "[Decision] Should ObjectKanban get a real record-source ladder? — KanbanSchema.data is any[], so it has no data binding to resolve #7651 was ruled B and closed as not_planned (2026-09-05T02:09:54Z)" — RIGHT (closed_at matches to the second).
  16. Prior reviews re-measured. Review 1's three changeset defects and review 2's one ("permanent by decision", 8m21s after the ruling — 02:18:01Z − 02:09:40Z verified) all reproduce under my instrument; neither REFUSE was wrong. Review 2's item 13 scope claim reproduces (one path). Nothing to overturn.

② Semver grading

Declared '@object-ui/types': minor (changeset line 2). Correct. AGENTS.md §版本号策略 line 239: 「changeset 里不要声明 major … objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」, enforced by check-changeset-no-major.mjs — exit 0 at head (✅ No changeset declares a \major` bump.), lit control with a planted majorchangeset → exit 1 with the gate's own❌ A changeset declares a `major` bump:(control removed, tree clean). The rule's second half is met: the body spells out both measured effects — the loud TS2741 break for consumers who flowed the entry into the declarative face, and the silentanyon declared-member reads via the index signature — and closes by naming it a breaking change shipped asminorunder the alignment rule.check-changeset-presence exit 0 (3 source file(s) of 2 released package(s) … 1 changeset(s)); @object-ui/plugin-kanbancorrectly has no entry (unpublished test file only, samefixedgroup). CIChangeset Bump Policy, Changeset Declaration, Changeset Overwrite Report, Changeset Fixed Group Check`: success at head.

③ Boundary-flag disposition

VERDICT: PASS


Generated by Claude Code

@os-sam
os-sam marked this pull request as ready for review September 5, 2026 07:00
@os-sam
os-sam added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit bc640ec Sep 5, 2026
34 checks passed
@os-sam
os-sam deleted the claude/issue-7645-schema-registry-kanban-honesty branch September 5, 2026 07:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants